//package vmmjava;

import java.net.*;
import java.io.*;
import java.util.*;
import java.util.concurrent.*;
import java.sql.*;
import java.text.SimpleDateFormat;

// XML related imports
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.output.XMLOutputter;
import org.jdom.output.Format;

// BEGIN_CLASS: Server that listens for connections 
public class VMManager {
 
 private String name;
 private String version;
 private int serverPort;
 private int totalActiveVM;
 private ConcurrentHashMap<Integer,Process> map;
 
 // VMManager constructor
 public VMManager(String name, String version, String serverPort) {
	this.name					= name;
	this.version			= version;
	this.serverPort		= Integer.parseInt(serverPort);
	this.map					= new ConcurrentHashMap<Integer,Process>();
	this.totalActiveVM = 0;
	// Deletes all possible left entries in table instances
	this.cleanInstances();
 }
 // Main method that starts a new Thread for each connection
 public static void main(String args[]) {
	if(args.length != 1) System.out.println("Use: java VMManager port");
	else {
		try {
			VMManager manager = new VMManager("VMManager", "1.0", args[0]);
			if(manager.getPort() <= 1024) throw new Exception("Error: port must be a number greater than 1024");
			manager.run();
		} catch (NumberFormatException nfe) {
			System.out.println("Error: port must be a number");
		} catch (Exception ex) {
			if(ex.getMessage() != null) System.out.println(ex.getMessage());
			else System.out.println("Error: unknown exception occurred");
		}
	}
 }

 // Endless loop that waits for client connections
 private void run() {
	try {
	// Get a server socket
	ServerSocket server = this.getServerSocket();
	// Let us know that you're listening
	System.out.println(name +" is listening on port "+ this.serverPort +".");

	do {
		// Accept a connection - this method waits for connections
		Socket client = server.accept();
		// Handle every connection with a separate thread
		(new VMManagerThread(this, client)).start();
	} while(true);

	} catch(Exception ex) {
	if(ex.getMessage() != null) System.out.println(ex.getMessage());
	else System.out.println("Error: unknown exception occurred");
	}
 }

// Deletes all entries in table instances
 public boolean cleanInstances() {
	try { 
		 MySQLAccess mysql = new MySQLAccess();
		 Connection conn	= mysql.getConnection();
		 Statement stmt		= conn.createStatement();
		 stmt.executeUpdate("DELETE FROM instances");
		 stmt.close();
		 conn.close();
		 return true;
 
	} catch(SQLException sqle) {
		 if(sqle.getMessage() != null) System.out.println("Error: "+ sqle.getMessage());
		 else System.out.println("Error: unknown exception occurred");
		 return false;
	} catch(Exception ex) {
		 if(ex.getMessage() != null) System.out.println("Error: "+ ex.getMessage());
		 else System.out.println("Error: unknown exception occurred");
		 return false;
	}
 }

 // Get a server socket
 private ServerSocket getServerSocket() throws Exception {
	return new ServerSocket(serverPort);
 }

 public void registerProcess(String mid, Process p) {
	this.map.put(new Integer(Integer.parseInt(mid)), p);
 }

 public Process getProcess(String mid) {
		return this.map.get(new Integer(Integer.parseInt(mid)));
 }

 // Returns how many VMs are running on the system
 public int getActiveVMs() {
	return this.totalActiveVM;
 }

 // Increases internal VM counter
 public void incrActiveVMs() {
	this.totalActiveVM++;
 }

 // Decresses internal VM counter
 public void decrActiveVMs() {
	this.totalActiveVM--;
 }

 // Returns port where server is listening to
 public int getPort() {
	return this.serverPort;
 }

 public void resetTotalVM() {
	this.totalActiveVM = 0;
 }

}// END_CLASS: VMManager


/* *********************************************************** */

// BEGIN_CLASS: Handle a single server connection
class VMManagerThread extends Thread {

 private Document xml_request;
 private Socket client;
 private VMManager manager;
 private String string_call;

 private String function_name;
 private ArrayList<String> args;

 // Communication channel with the process
 private VMManagerInputStream processIn; 

 // Comunication channel with the client
 private VMManagerOutputStream clientOut;
 private VMManagerInputStream	clientIn; 
 
 // Keep track of the client socket and the manager
 public VMManagerThread(VMManager manager, Socket client) {
	this.manager = manager; 
	this.client	= client;
	this.args		= new ArrayList<String>();
 }
 // Thread entry point
 public void run() {
	try {
	// Display info about the connection
	this.describeConnection(this.client);
	// Create streams to send/recive data from/to the client
	this.clientOut = new VMManagerOutputStream(this.client);
	this.clientIn	= new VMManagerInputStream(this.client);
	// Get the client's request
	this.xml_request = this.clientIn.buildXMLfromRequest();
	// Display the XML request
	//MyXMLOutputter.outputDocument(this.xml_request, System.out);
	// Process request 
	this.exec();
	// Close streams and database connection
	this.clientOut.close();
	this.clientIn.close();
	this.client.close();
	
	} catch(IOException ioex) {
		System.out.println("IOException occurred when processing request.");
		if(ioex.getMessage() != null) System.out.println(ioex.getMessage());
	} catch(JDOMException jde) {
		System.out.println("JDOMExeption occurred when processing request.");
		// send message to the client informing about the error
		if(jde.getMessage() != null) System.out.println(jde.getMessage());
		clientOut.sendMessage("3",jde.getMessage(),""); 
	} catch(Exception ex) {
		if(ex.getMessage() != null) System.out.println(ex.getMessage());
		else System.out.println("Error: unknown exception occurred");
	}
 }

 // Register a user, starts VM if needed, and sets ip tables 
 // NOTE: we wait until VM has started to register user in the DB
 public void login(String vmid, String source_ip, String source_port) {
	try {
		// Connect to database
		MySQLAccess mysql	= new MySQLAccess();
		Connection conn		 = mysql.getConnection();
		boolean grantAccess = false;
		boolean isRunning	= false;
		ResultSet result;
		
		// Check if the IP is filtered	
		Statement stmt = conn.createStatement();
		String query	= "SELECT ipfilter.ipaddr, ipfilter.allow FROM ipfilter "+
										 "INNER JOIN mapping ON ipfilter.mid=mapping.mid "+
										 "WHERE ipfilter.ipaddr='"+ source_ip +"'";
		result = stmt.executeQuery(query);
		
		// IP is filtered
		if(result.next()) {
 
		 // Allowed or Denied?
			String allow = result.getString("allow");
			String ip		= result.getString("ipaddr");
			grantAccess	= allow.equals("1"); 

			if(grantAccess) System.out.println("IP "+ source_ip +" allowed");
			else System.out.println("IP "+ source_ip +" not allowed");

		} 

		// IP is not filtered
		else {

			// Check if allow ALL is set
			stmt	= conn.createStatement();
			query = "SELECT ipfilter.ipaddr, ipfilter.allow FROM ipfilter "+
					    "INNER JOIN mapping ON ipfilter.mid=mapping.mid "+
					    "WHERE ipfilter.ipaddr='0.0.0.0'";

			result = stmt.executeQuery(query);
			if(result.next()) grantAccess = true;
			else System.out.println("Access denied: ip "+ source_ip +" can't connect to the system");

		}

		// Access is allowed (either explicitly or allow all)
		if(grantAccess) {

			// Get VM location on disk and assigned IP
			stmt	= conn.createStatement();
			query	= "SELECT mid, vm_ip, vm_path FROM mapping WHERE vmid='"+ vmid +"'";
			result = stmt.executeQuery(query);
		
			// User has an account in the system
			if(result.next()) {

				// Get user VM parameters
				String mid		 = result.getString("mid");
				String vm_path = result.getString("vm_path");
				String vm_ip	= result.getString("vm_ip");
				
				// Check if the VM is already running
				stmt	= conn.createStatement();
				query	= "SELECT mid FROM instances WHERE mid="+ mid;
				result = stmt.executeQuery(query);
				isRunning = (result.next());

				// Recheck using ping the newly created VM and wait for an answer
				int timeout_rc = 2000;

				InetAddress address_rc = InetAddress.getByName(vm_ip);
				System.out.println("Ping to recheck if VM is up at "+vm_ip);

				if(address_rc.isReachable(timeout_rc)) System.out.println("Ping ok!");
				else isRunning = false;


				// No other user is running the VM. It has to be started up and iptables set
				if(!isRunning) {

					// Start VM
					List<String> command = new ArrayList<String>();
					command.add("/bin/sh");
					command.add(vm_path+"/launch.sh");

					System.out.println("Booting " + vm_path + " [" + vmid + "]");
					
					// Create and execute the new process 
					ProcessBuilder builder = new ProcessBuilder(command);

					// Redirects stderror to stdout
					builder.redirectErrorStream(true);
					Process process = builder.start();

					// Set comunication channel for the process and get its output
					this.processIn = new VMManagerInputStream(process);

					// Ping the newly created VM and wait for an answer
					int timeout = 15000;

					InetAddress address = InetAddress.getByName(vm_ip);
					System.out.println("Ping the new VM...."+vm_ip);

					if(address.isReachable(timeout)) System.out.println("Ping ok!");
					//else System.out.println("Ping failed, VM might not be accessible");
					else throw new TimeoutException("Ping failed!");

					// Register the process
					this.manager.registerProcess(mid, process);
         // Register User
					query = "INSERT INTO instances (mid, source_ip, source_port) VALUES ("+
								  mid +",'"+ source_ip +"','"+ source_port +"')";
					stmt.executeUpdate(query);

					// Update history table
					stmt	= conn.createStatement();
					query	= "SELECT iid FROM instances WHERE mid="+ mid +
								 " AND source_ip='"+ source_ip +"' AND source_port='"+ source_port +"'"; 
					result = stmt.executeQuery(query);

					if(result.next()) {
						String iid = result.getString("iid");
						stmt	= conn.createStatement();
						query	= "INSERT INTO history (iid, mid, source_ip, source_port) VALUES ("+
										 iid +","+ mid +",'"+ source_ip +"','"+ source_port +"')";
						stmt.executeUpdate(query);
					} else System.out.println("History table not updated: inconsistency found, please clean table");
					
					// Increments number of VM running on the system
					this.manager.incrActiveVMs();
					System.out.println("There is/are "+ this.manager.getActiveVMs() +" VM running in the system");
					// Send message to the client
					clientOut.sendMessage("0","VM stared ok!", Integer.toString(manager.getActiveVMs())); 

				// The user's VM is already running
				} else { 

					// Ping the newly created VM and wait for an answer
					int timeout = 5000;

					InetAddress address = InetAddress.getByName(vm_ip);
					System.out.println("Ping the new VM...."+vm_ip);

					if(address.isReachable(timeout)) System.out.println("Ping ok!");
					//else System.out.println("Ping failed, VM might not be accessible");
					else throw new TimeoutException("Ping failed!");


					// Register User
					stmt	= conn.createStatement();
					query = "INSERT INTO instances (mid, source_ip, source_port) VALUES ("+
								mid +",'"+ source_ip +"','"+ source_port +"')";
					stmt.executeUpdate(query);

					// Update history table
					stmt	= conn.createStatement();
					query	= "SELECT iid FROM instances WHERE mid="+ mid +
								 " AND source_ip='"+ source_ip +"' AND source_port='"+ source_port +"'"; 
					result = stmt.executeQuery(query);

					if(result.next()) {
						String iid = result.getString("iid");
						stmt	= conn.createStatement();
						query	= "INSERT INTO history (iid, mid, source_ip, source_port) VALUES ("+
										 iid +","+ mid +",'"+ source_ip +"','"+ source_port +"')";
						stmt.executeUpdate(query);
					} else System.out.println("History table not updated: inconsistency found, please clean table");
					
					System.out.println("VM is already running no VM startup needed...");
					clientOut.sendMessage("0","VM is already running no VM startup needed","");
				}


					// Set Iptables
					setIptables(vmid, source_ip, "add");
					System.out.println("Setting iptables....ok!");
					stmt	= conn.createStatement();

 
			// There is no entry in the database for this user
			} else {

				System.out.println("The user has not been registered in the system");
				System.out.println("No VM has been started");
				clientOut.sendMessage("6","User has not been registered or invalid vmid","");

			}

		// User IP is not allowed to login
		} else {

			System.out.println("User can't login, ip filtered"); 
			clientOut.sendMessage("5","Ip is filtered and not allowed to login", source_ip);

		}

		conn.close();
		result.close();
		
	} catch(SQLException sqle) { 

		System.out.println("Error: sql statement error!");
		clientOut.sendMessage("14",sqle.getMessage(),""); 

	} catch(IOException ioe) {

		System.out.println("Error: I/O error!");
		if(ioe.getMessage() != null) System.out.println(ioe.getMessage());
		clientOut.sendMessage("8",ioe.getMessage(),""); 

	} catch(NullPointerException npe) {

		System.out.println("Error: null argument was passed to the command!");
		if(npe.getMessage() != null) System.out.println(npe.getMessage());
		clientOut.sendMessage("9",npe.getMessage(),"");

	} catch(IndexOutOfBoundsException iobe) {

		System.out.println("Error: out-of-bounds");
		clientOut.sendMessage("10",iobe.getMessage(),"");

	} catch(TimeoutException toe) {

		System.out.println("Error: VM ping timeout, try again later");
		clientOut.sendMessage("7","Ping timeout","");

	} catch(Exception ex) { 

		if(ex.getMessage() != null) System.out.println("Error: "+ ex.getMessage());
		else System.out.println("Error: unknown exception occurred");
		clientOut.sendMessage("4",ex.getMessage(),"");

	}
 }


 // Unregister user from the system, stops VM if necessary, and set iptables back to normal values
 public void logout(String vmid, String source_ip, String source_port) {

	try {

		//Connect to database
		MySQLAccess mysql = new MySQLAccess();
		Connection conn	= mysql.getConnection();

		// Check if is a shutdown

		if(source_port.equals("0")) {

			System.out.println("Executing VM Shutdown of "+ vmid + "@" + source_ip);
			
			Statement stmt = conn.createStatement();
			String query	= "SELECT * FROM mapping AS m "+
					  "WHERE m.vmid='"+ vmid +"' "+
					  "AND m.vm_ip='"+ source_ip +"';";
	
			ResultSet result = stmt.executeQuery(query);

			// It seems like a valid shutdown
			if(result.next()) {

				System.out.println("Valid shutdown!");
				// Wait 1 seconds (VM is halting)
//				Thread.sleep(10*1000);
				
				// Stop VM and delete iptables rule
				setIptables(vmid, source_ip, "clear");

				// The VM will halt and delete his rules by itself

//				Process process = this.manager.getProcess(mid);

				// The VM is running
//				if(process != null) {
//
//					System.out.println("Stopping VM...");
//					process.destroy();
//
					// Decrement active VMs
					this.manager.decrActiveVMs();
					System.out.println("There is/are "+ this.manager.getActiveVMs() +" VM running in the system");
					clientOut.sendMessage("0","VM stoped",Integer.toString(this.manager.getActiveVMs()));

					return;
//
//				// We can't find VM
//				} else {
//
//					System.out.println("The VM is not running, can't terminate the process");
//					clientOut.sendMessage("12","Can't stop VM, it's not there", iid); 
//
//				}
			}
		}


		
		// Check if the user is truly loged in
		Statement stmt = conn.createStatement();
		String query	= "SELECT instances.iid, instances.mid FROM instances "
				+"INNER JOIN mapping ON instances.mid=mapping.mid "
				+"WHERE mapping.vmid='"+ vmid +"' "
				+"AND instances.source_ip='"+ source_ip +"'";
//										"AND instances.source_ip='"+ source_ip +"' "+
//										"AND instances.source_port='"+ source_port +"'";
		ResultSet result = stmt.executeQuery(query);
	
		System.out.println("Executing VM logout of "+ vmid + " for " + source_ip);

		// The user is logged in
		if(result.next()) {

			String mid = result.getString("mid");
			String iid = result.getString("iid");

			// Delete user from database (instances table)
			stmt	= conn.createStatement();
			query = "DELETE FROM instances WHERE iid="+ iid;
			stmt.executeUpdate(query);

			// Update history table - logout time		 
			java.util.Date now	= new java.util.Date();
			SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			String now_str			= fmt.format(now);
		 
			stmt	= conn.createStatement();
			query = "UPDATE history SET time_closed='"+ now_str +"' WHERE iid="+ iid;
			stmt.executeUpdate(query);
 
			// Delete rule
			setIptables(vmid, source_ip, "del");

//			stmt	= conn.createStatement();
//			query = "SELECT instances.mid FROM instances "+
//							"INNER JOIN mapping ON instances.mid=mapping.mid "+
//							"WHERE mapping.vmid='"+ vmid +"'";
//			result = stmt.executeQuery(query);			

			// There are still users using the VM
//			} else {
//
//				System.out.println("Not stopping VM, there are still users using it!");
//				clientOut.sendMessage("0","Not stopping VM, there are still users using it","");
//			}

		// The user was not loged in
		} else {

			System.out.println("Can't logout user. User is not loged in!");
			clientOut.sendMessage("11","Can't log out user, is not loged in","");

		}

		// Close Database connection
		conn.close();
		result.close();
			
	} catch(SQLException sqle) { 

		System.out.println("Error: sql statement error!");
		clientOut.sendMessage("14",sqle.getMessage(),"");

	} catch(IOException ioe) {

		System.out.println("Error: I/O error!");
		if(ioe.getMessage() != null) System.out.println(ioe.getMessage());
		clientOut.sendMessage("8",ioe.getMessage(),"");

	} catch(Exception ex) { 

		if(ex.getMessage() != null) System.out.println("Error: "+ ex.getMessage());
		else System.out.println("Error: unknown exception occurred");
		clientOut.sendMessage("4",ex.getMessage(),"");

	}
 }

 // Parses an XML request and execute command 
 public void exec() {
	// Get function name
	Element root = this.xml_request.getRootElement();
	// Get arguments
	try {
		 String param;
		 this.function_name = root.getChildTextTrim("function_name");
		 this.string_call	= this.function_name + "(";
			// login or logout
		 if(function_name.equals("login") || function_name.equals("logout")) {

			if((param = root.getChild("arguments").getChildTextTrim("vmid"))	!= null) {
				 this.args.add(param);
				 this.string_call += "\"vmid\",";
			} 

			if((param = root.getChild("arguments").getChildTextTrim("ip"))	!= null) {
				 this.args.add(param);
				 this.string_call += "\""+ param +"\",";
			}

			if((param = root.getChild("arguments").getChildTextTrim("port")) != null) {
				 this.args.add(param);
				 this.string_call += "\""+ param +"\")";
			} 

			if(this.function_name.equals("login"))	this.login(this.args.get(0), this.args.get(1), this.args.get(2));
			if(this.function_name.equals("logout")) this.logout(this.args.get(0), this.args.get(1), this.args.get(2));
		 }

		 // restart
		 else if(function_name.equals("restart")) { 
			this.string_call += ")";
			this.restart();
		 }
		 // unknown function 
		 else throw new NoFunctionException("XML request does not name a valid function");
	
	} catch(NoFunctionException nfe) {
		 System.out.println("Error: "+ nfe.getMessage());
		 clientOut.sendMessage("1",nfe.getMessage(), MyXMLOutputter.outputDocument(this.xml_request));
	} catch(Exception ex) {
		 System.out.println("Bad XML request");
		 clientOut.sendMessage("2","Bad xml request",MyXMLOutputter.outputDocument(this.xml_request));
	}
 }

 // Deletes all entries in table Instances
 private boolean flushInstances() {
		return this.manager.cleanInstances();
 } 

 // Adds or Deletes iptables rule
 private boolean setIptables(String vmid, String source_ip, String action) {
		
	final String https ="443";
	final String http	="80";
	String param;
	
	if		 (action.equals("add")) param = "I";
	else if(action.equals("del")) param = "D";
	else if(action.equals("clear")) param = "C";
	else return false;

	try {
		 MySQLAccess mysql	= new MySQLAccess();
		 Connection	conn	= mysql.getConnection();
		 Statement	stmt	= conn.createStatement();
		 String			query	= "SELECT vm_ip, vm_path FROM mapping WHERE vmid=\'"+ vmid +"\'";
		 ResultSet	result = stmt.executeQuery(query);
	
		 if(result.next()) {
			String vm_ip = result.getString("vm_ip");
			String vm_path = result.getString("vm_path");

			System.out.println("Rules of " + vm_ip);

			List<String> command = new ArrayList<String>();
			command.add("/bin/bash");
			command.add(vm_path+"/set-iptables");
			command.add(param);		 // add/del/clear
			command.add(http);		 // port
			command.add(http);		 // dport
			command.add(vm_ip);		 // destination ip
			command.add("any");		 // sport
			command.add(source_ip);// source_ip
			ProcessBuilder builder = new ProcessBuilder(command);
			builder.redirectErrorStream(true);
			Process process = builder.start();

			// Set comunication channel for the process and get its output
			VMManagerInputStream in = new VMManagerInputStream(process);
			System.out.println(in.getMessage());

			command.clear();

			command.add("/bin/bash");
			command.add(vm_path+"/set-iptables");
			command.add(param);		 // add/del
			command.add(https);		 // port
			command.add(https);		 // dport
			command.add(vm_ip);		 // destination_ip
			command.add("any");		 // sport
			command.add(source_ip);// sip
			
	builder = new ProcessBuilder(command);
	builder.redirectErrorStream(true);
		process = builder.start();
			// Set comunication channel for the process and get its output
			in = new VMManagerInputStream(process);
			System.out.println(in.getMessage());
			return true;
		 } 
		 else return false;
	} catch(SQLException sqle) {
		 System.out.println("Error: "+ sqle.getMessage()); 
	} catch(Exception ex) {
		 System.out.println("Error: "+ ex.getMessage());
	}
	return false;
 }

 // Private function that kills all active VMs
 //TODO: update history table
 private void restart() {
	
	try {
		 // Connect to database
		 MySQLAccess mysql = new MySQLAccess();
		 Connection conn	= mysql.getConnection();
		
		 // Join between nd Instances to get the vmids of currently running VMs
		 Statement stmt	= conn.createStatement();
		 String query		 = "SELECT DISTINCT mid FROM instances";
		 ResultSet result = stmt.executeQuery(query);

		 while(result.next()) {
			String mid = result.getString("mid");
			Process p	= manager.getProcess(mid);
			if(p != null) { 
				 p.destroy();
				 System.out.println("VM from user "+ mid +" stopped");
			} else System.out.println("There is no VM running for user "+ mid);
		 }
		 this.manager.resetTotalVM();
		 // Close connection
		 stmt.close();
		 conn.close();
 
	} catch(SQLException sqle) { 
		System.out.println("Error: sql statement error!");
		if(sqle.getMessage() != null) System.out.println(sqle.getMessage());
	} catch(Exception ex) { 
		if(ex.getMessage() != null) System.out.println("Error: "+ ex.getMessage());
		else System.out.println("Error: unknown exception occurred");
	}
 }

 // Adds a new VM to the system
 public boolean addVM(String vmid, String ip, String host, String path) {
	return true;
 }

 // Configure Database
 public boolean configureManager(String host, String user, String password) {
	return true;
 }

 // Returns a string representation of the XML request
 String getStringCall() {
	return this.string_call;
 }

 // Display info about the connection
 void describeConnection(Socket cli) {
	String destName = cli.getInetAddress().getHostName();
	String destAddr = cli.getInetAddress().getHostAddress();
	int destPort		= cli.getPort();
	System.out.println("Accepted connection to "+destName+" ("+destAddr+")"+" on port "+destPort+".");
 }

 }// END_CLASS: VMManagerThread


class TimeoutException extends Exception {
	TimeoutException(String msg) {
		super(msg);
	}
}

class NoFunctionException extends Exception {
	NoFunctionException(String msg) {
		super(msg);
	}
}


/* *********************************************************** */
 
// BEGIN_CLASS: convenience class for reading client requests
class VMManagerInputStream extends BufferedReader {

	public VMManagerInputStream(Socket client) throws IOException {
		super(new InputStreamReader(client.getInputStream()));
	}

	public VMManagerInputStream(Process process) throws IOException {
		super(new InputStreamReader(process.getInputStream()));
	}

	// Get the request and builds a XML document with it 
	public Document buildXMLfromRequest() throws IOException, JDOMException {

		String line;
		StringBuffer request = new StringBuffer();
		while((line = this.readLine()).length() > 0) request.append(line+"\n");

		// Creates and returns an XML Document from the string
		return (new SAXBuilder()).build(new StringReader(request.toString()));
	}

	public String getMessage() throws IOException {

		String line;
		StringBuffer msg = new StringBuffer();
		while((line = this.readLine()) != null) msg.append(line+"\n");
		return msg.toString();
	}
}// END_CLASS: VMManagerInputStream


/* *********************************************************** */

// BEGIN_CLASS: convinience class for sending requests 
class VMManagerOutputStream extends PrintWriter {
	// true = autoflush
	public VMManagerOutputStream(Socket client) throws IOException {
		super(client.getOutputStream(), true);
	} 
	
	// Transforms a string to a XML file and writes it to the output stream
	public void sendMessage(String result, String info, String extra) {
		// Creates XML docuemnt
		Document xml_file = this.buildXMLfromMessage(result, info, extra);
		String xml_string = MyXMLOutputter.outputDocument(xml_file);
		//System.out.println(xml_string);
		this.println(xml_string);
	}

	public Document buildXMLfromMessage(String result, String info, String extra) {
		Document xml = new Document(new Element("remote_call_return"));
		Element resElement = new Element("result");
		resElement.setText(result.trim());
		Element infElement = new Element("info");
		infElement.setText(info.trim());
		Element extElement = new Element("extra");
		extElement.setText(extra.trim());
		xml.getRootElement().addContent(resElement);
		xml.getRootElement().addContent(infElement);
		xml.getRootElement().addContent(extElement);
		return xml;
	}
}// END_CLASS: VMManagerOutputStream


/* *********************************************************** */

// BEGIN_CLASS: convinience class for handling MySQL database connections
class MySQLAccess {
	
	private Connection conn = null;

	MySQLAccess() throws Exception { 
		// host, database, user, password
		connect("localhost", "vmmanager", "vmmanager", "$$PASSWORD$$");
	}

	public void connect(String host, String db, String user, String passwd) throws Exception {
		// This will load the MySQL driver, each DBMS has its own driver
		Class.forName("com.mysql.jdbc.Driver");
		// Setup the connection
		this.conn = DriverManager.getConnection("jdbc:mysql://"+ host +"/"+ db, user, passwd);
	}

	public Connection getConnection() {
		return this.conn;	
	}

	public void close() throws Exception {
		this.conn.close();
	}
}// END_CLASS: MySQLAccess

/* *********************************************************** */

// BEGIN_CLASS: convinience class for converting XML Documents to strings
class MyXMLOutputter {
// Prints XML document to standard output
 public static void outputDocument(Document myDocument, OutputStream out) {
	try {
		XMLOutputter outputter = new XMLOutputter(Format.getPrettyFormat());
		outputter.output(myDocument, System.out);
	} catch (IOException ioe) {
		 System.out.println("Error: couldn't print XML file to standard putput");
		 if(ioe.getMessage() != null) System.out.println(ioe.getMessage());
	}
 }

 // Prints XML document to a string
 public static String outputDocument(Document myDocument) {
		XMLOutputter outputter = new XMLOutputter(Format.getPrettyFormat());
		return outputter.outputString(myDocument);
 }
} // END_CLASS: XMLOutputter

